import math
import time
import random
import re
import json
import requests
import pandas as pd
from datetime import datetime


# 虚值度阈值映射表 - 对应标的代码的虚值度阈值(%)
VIR_VALUE_DEGREE_THRESHOLD_DICT = {
    510050: 5.0,    # 上证50ETF
    510300: 10.0,   # 沪深300ETF
    159919: 10.0,   # 沪深300ETF
    510500: 15.0,   # 中证500ETF
    159922: 15.0,   # 中证500ETF
    588000: 20.0,   # 科创50ETF
    588080: 20.0,   # 科创50ETF
    159901: 20.0,   # 深证100ETF
    159915: 20.0,   # 创业板ETF
}

ANNUALIZED_RETURN_THRESHOLD_DICT = {
    "认购": 20.0,
    "认沽": 30.0,
}

EXCHANGE_DICT = {"SSE": "m:10", "SZSE": "m:12"}

def option_value_analysis_em(exchange: str = "SSE") -> pd.DataFrame:

    if exchange not in EXCHANGE_DICT:
        raise ValueError("exchange 只能是 'SSE' 或 'SZSE'")

    fs = EXCHANGE_DICT[exchange]
    pz = 50
    base_url = (
        "https://push2.eastmoney.com/api/qt/clist/get?"
        "fid=f301&po=1&np=1&fltt=2&invt=2"
        "&ut=8dec03ba335b81bf4ebdf7b29ec27d15"
        "&fields=f1,f2,f3,f12,f13,f14,f298,f299,f249,f300,f330,f331,f332,f333,f334,f335,f336,f301,f152"
        f"&fs={fs}&pz={pz}&pn={{page}}"
    )
    headers = {
        "Referer": "https://data.eastmoney.com/other/valueAnal.html",
        "User-Agent": (
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
            "AppleWebKit/537.36 (KHTML, like Gecko) "
            "Chrome/137.0.0.0 Safari/537.36"
        ),
    }

    session = requests.Session()
    resp = session.get(base_url.format(page=1), headers=headers, timeout=10)
    match = re.search(r"\{.*\}\}$", resp.text)
    if not match:
        raise ValueError("未找到有效 JSON 数据")
    js = json.loads(match.group())
    total = js["data"]["total"]
    pages = math.ceil(total / pz)

    data = js["data"]["diff"]

    for page in range(2, pages + 1):
        resp = session.get(base_url.format(page=page), headers=headers, timeout=10)
        match = re.search(r"\{.*\}\}$", resp.text)
        if not match:
            continue
        js = json.loads(match.group())
        data.extend(js["data"]["diff"])
        time.sleep(0.3 + random.random() * 0.2)

    temp_df = pd.DataFrame(data)
    # print(f"DataFrame 列数: {len(temp_df.columns)}")  # 保留调试信息
    temp_df.columns = [
        # f1, f2, f3, f12, f13, f14, f152, f249, f298, f299, f300, f301, f330, f331, f332, f333, f334, f335, f336
        "id",  # f1
        "最新价",  # f2
        "涨幅",  # f3
        "期权代码",  # f12
        "f13",  # f13
        "期权名称",  # f14
        "f152",
        "隐含波动率",  # f249
        "时间价值",  # f298
        "内在价值",  # f299
        "理论价格",  # f300
        "到期日",  # f301
        "f330",  # f330
        "标的代码",  # f331
        "f332",  # f332
        "标的名称",  # f333
        "标的最新价",  # f334
        "标的涨幅",  # f335
        "标的近一年波动率",  # f336
    ]
    temp_df = temp_df[
        [
            "期权代码",
            "期权名称",
            "最新价",
            "时间价值",
            "内在价值",
            "隐含波动率",
            "理论价格",
            "标的代码",
            "标的名称",
            "标的最新价",
            "标的近一年波动率",
            "到期日",
        ]
    ]
    temp_df["最新价"] = pd.to_numeric(temp_df["最新价"], errors="coerce")
    temp_df["时间价值"] = pd.to_numeric(temp_df["时间价值"], errors="coerce")
    temp_df["内在价值"] = pd.to_numeric(temp_df["内在价值"], errors="coerce")
    temp_df["隐含波动率"] = pd.to_numeric(temp_df["隐含波动率"], errors="coerce")
    temp_df["理论价格"] = pd.to_numeric(temp_df["理论价格"], errors="coerce")
    temp_df["标的最新价"] = pd.to_numeric(temp_df["标的最新价"], errors="coerce")
    temp_df["标的近一年波动率"] = pd.to_numeric(
        temp_df["标的近一年波动率"], errors="coerce"
    )
    temp_df["到期日"] = pd.to_datetime(
        temp_df["到期日"].astype(str), errors="coerce"
    ).dt.date
    return temp_df


def calculate_option_margin(df: pd.DataFrame, contract_unit: int = 10000,
                            broker_margin_ratio: float = 0.2) -> pd.DataFrame:
    """
    计算期权开仓保证金和维持保证金

    参数:
    df: 期权数据DataFrame，应包含'期权名称'、'最新价'、'标的最新价'列
    contract_unit: 合约单位，默认10000
    broker_margin_ratio: 券商在交易所基础上增加的保证金比例，默认0.2 (20%)

    返回:
    添加了保证金计算结果的DataFrame
    """
    # 从期权名称中提取行权价
    pattern = re.compile(r'^(.+?)([沽购])(\d{1,2})月(\d{3,4})')
    df['行权价'] = df['期权名称'].apply(lambda x: float(pattern.match(x).group(4)) / 1000.0 if pattern.match(x) else None)

    # pattern = re.compile(r'^(.+?)([沽购])(\d{1,2})月(\d{3,4})')
    # matcher = pattern.match(option_value['option_name'])
    # if matcher:
    #     # 提取行权价并转换为浮点数（除以1000）
    #     option_value['strike_price'] = float(matcher.group(4)) / 1000.0
    #     print(f"行权价: {option_value['strike_price']}")
    # else:
    #     print("未能匹配期权名称模式")
    # df['行权价'] = df['期权名称'].str.extract(r'(\d+\.\d+|\d+)').astype(float)

    # 判断期权类型(认购/认沽)
    df['期权类型'] = df['期权名称'].apply(lambda x: '认购' if '购' in x else '认沽')

    # 计算虚值额
    df['虚值额'] = 0.0
    # 认购期权虚值额 = max(行权价 - 标的最新价, 0)
    df.loc[df['期权类型'] == '认购', '虚值额'] = df.loc[df['期权类型'] == '认购'].apply(
        lambda row: max(row['行权价'] - row['标的最新价'], 0), axis=1
    )
    # 认沽期权虚值额 = max(标的最新价 - 行权价, 0)
    df.loc[df['期权类型'] == '认沽', '虚值额'] = df.loc[df['期权类型'] == '认沽'].apply(
        lambda row: max(row['标的最新价'] - row['行权价'], 0), axis=1
    )

    # 计算交易所开仓保证金
    df['交易所开仓保证金'] = 0.0
    # 认购期权开仓保证金
    call_margin = df.loc[df['期权类型'] == '认购'].apply(
        lambda row: round((row['最新价'] + max(0.12 * row['标的最新价'] - row['虚值额'], 0.07 * row['标的最新价'])) * contract_unit, 2),
        axis=1
    )
    df.loc[df['期权类型'] == '认购', '交易所开仓保证金'] = call_margin

    # 认沽期权开仓保证金
    put_margin = df.loc[df['期权类型'] == '认沽'].apply(
        lambda row: round(min(
            row['最新价'] + max(0.12 * row['标的最新价'] - row['虚值额'], 0.07 * row['行权价']),
            row['行权价']
        ) * contract_unit, 2),
        axis=1
    )
    df.loc[df['期权类型'] == '认沽', '交易所开仓保证金'] = put_margin

    # 计算交易所维持保证金(假设结算价等于最新价)
    df['交易所维持保证金'] = 0.0
    # 认购期权维持保证金
    call_maintenance = df.loc[df['期权类型'] == '认购'].apply(
        lambda row: round((row['最新价'] + max(0.12 * row['标的最新价'] - row['虚值额'], 0.07 * row['标的最新价'])) * contract_unit, 2),
        axis=1
    )
    df.loc[df['期权类型'] == '认购', '交易所维持保证金'] = call_maintenance

    # 认沽期权维持保证金
    put_maintenance = df.loc[df['期权类型'] == '认沽'].apply(
        lambda row: round(min(
            row['最新价'] + max(0.12 * row['标的最新价'] - row['虚值额'], 0.07 * row['行权价']),
            row['行权价']
        ) * contract_unit, 2),
        axis=1
    )
    df.loc[df['期权类型'] == '认沽', '交易所维持保证金'] = put_maintenance

    # 计算券商保证金 = 交易所保证金 * (1 + 券商增加比例)
    df['券商开仓保证金'] = round(df['交易所开仓保证金'] * (1 + broker_margin_ratio), 2)
    df['券商维持保证金'] = round(df['交易所维持保证金'] * (1 + broker_margin_ratio), 2)

    return df

def calculate_option_value(df: pd.DataFrame, contract_unit: int = 10000,
                           virtual_threshold_map: dict = VIR_VALUE_DEGREE_THRESHOLD_DICT,
                           return_threshold_map: dict = ANNUALIZED_RETURN_THRESHOLD_DICT
                           ) -> pd.DataFrame:
    # 计算虚值度
    df['虚值度'] = 0.0
    # 认购期权虚值度
    df.loc[df['期权类型'] == '认购', '虚值度'] = df.loc[df['期权类型'] == '认购'].apply(
        lambda row: 0 if row['标的最新价'] == 0 or row['行权价'] == 0 else
        ((row['行权价'] / row['标的最新价']) - 1) * 100 if row['标的最新价'] < row['行权价'] else 0,
        axis=1
    )
    # 认沽期权虚值度
    df.loc[df['期权类型'] == '认沽', '虚值度'] = df.loc[df['期权类型'] == '认沽'].apply(
        lambda row: 0 if row['标的最新价'] == 0 or row['行权价'] == 0 else
        ((row['标的最新价'] / row['行权价']) - 1) * 100 if row['标的最新价'] > row['行权价'] else 0,
        axis=1
    )
    df['虚值度'] = df['虚值度'].round(2)

    # 计算最大保证金
    df['保证金'] = df[['券商开仓保证金', '券商维持保证金']].max(axis=1)

    # 计算收益率 (%)
    df['收益率'] = round(100 * df['最新价'] * contract_unit / df['保证金'], 2)

    # 计算剩余天数
    today = datetime.now().date()
    df['剩余天数'] = (pd.to_datetime(df['到期日']) - pd.to_datetime(today)).dt.days

    # 计算年化收益率 (%)
    df['年化收益率'] = round(df['收益率'] * 365 / df['剩余天数'], 2)

    # 为每行添加对应的虚值度阈值
    df['虚值度阈值'] = df['标的代码'].map(virtual_threshold_map)
    # 为每行添加对应的年化收益率阈值（基于期权类型）
    df['年化收益率阈值'] = df['期权类型'].map(return_threshold_map)
    return df


def filter_by_virtual_value_degree(df: pd.DataFrame,
                                   virtual_threshold_map: dict = VIR_VALUE_DEGREE_THRESHOLD_DICT,
                                   return_threshold_map: dict = ANNUALIZED_RETURN_THRESHOLD_DICT) -> pd.DataFrame:
    """
    根据虚值度阈值、年化收益率阈值和期权期限过滤期权数据

    参数:
    df: 期权数据DataFrame，应包含'标的代码'、'虚值度'、'年化收益率'、'期权类型'和'到期日'列
    virtual_threshold_map: 虚值度阈值映射表
    return_threshold_map: 年化收益率阈值映射表

    返回:
    过滤后的DataFrame
    """


    # 创建默认掩码，初始为True（保留所有行）
    mask = pd.Series([True] * len(df), index=df.index)

    # 遍历每个标的代码的虚值度阈值
    for underlying_code, threshold in virtual_threshold_map.items():
        underlying_mask = df['标的代码'] == underlying_code
        mask = mask & (~underlying_mask | (df['虚值度'] >= threshold))

    # 遍历每个期权类型的年化收益率阈值
    for option_type, threshold in return_threshold_map.items():
        option_mask = df['期权类型'] == option_type
        mask = mask & (~option_mask | (df['年化收益率'] >= threshold))

    # 转换到期日为datetime类型并处理异常
    df['到期日'] = pd.to_datetime(df['到期日'], errors='coerce', format='%Y-%m-%d')

    # 过滤无效日期
    invalid_dates = df['到期日'].isna()
    if invalid_dates.any():
        print(f"警告: 发现{invalid_dates.sum()}个无效日期，已过滤")
        df = df[~invalid_dates].copy()
        mask = mask[~invalid_dates]

    # 计算剩余天数
    today = pd.Timestamp.now()
    df['剩余天数'] = (df['到期日'] - today).dt.days

    # 按期权类型分别处理期限过滤
    call_options_mask = df['期权类型'] == '认购'
    put_options_mask = df['期权类型'] == '认沽'

    # 认购期权：保留最近两个月
    if call_options_mask.sum() > 0:
        min_days = df.loc[call_options_mask, '剩余天数'].min()
        call_keep_mask = call_options_mask & (df['剩余天数'] <= min_days + 60)
        mask = mask & (call_keep_mask | ~call_options_mask)

    # 认沽期权：保留最远月
    if put_options_mask.sum() > 0:
        max_days = df.loc[put_options_mask, '剩余天数'].max()
        put_keep_mask = put_options_mask & (df['剩余天数'] == max_days)
        mask = mask & (put_keep_mask | ~put_options_mask)

    # 应用掩码过滤数据
    filtered_df = df[mask].copy()
    print(f"过滤前数据量: {len(df)}, 过滤后数据量: {len(filtered_df)}")

    return filtered_df


def filter_by_option_info(df: pd.DataFrame, query: str) -> pd.DataFrame:
    """
    根据输入字符串自动选择过滤列：
    - 如果字符串是数字，则匹配期权代码列
    - 如果字符串是文本，则匹配期权名称列

    参数:
    df: 期权数据DataFrame，应包含'期权代码'和'期权名称'列
    query: 输入的查询字符串

    返回:
    过滤后的DataFrame
    """
    try:
        # 尝试将字符串转换为数字
        option_code = int(query)
        # 如果转换成功，按期权代码列过滤
        filtered_df = df[df['期权代码'] == option_code].copy()
        print(f"按期权代码过滤: {option_code}, 匹配结果: {len(filtered_df)} 条")
        return filtered_df
    except ValueError:
        # 如果转换失败，按期权名称列过滤（包含匹配）
        filtered_df = df[df['期权名称'].str.contains(query, na=False)].copy()
        print(f"按期权名称过滤: {query}, 匹配结果: {len(filtered_df)} 条")
        return filtered_df

def fetch_all_option_value() :
    df_sse = option_value_analysis_em("SSE")
    print(f"SSE结果")
    print(df_sse)
    time.sleep(1)  # 防止请求过快
    df_szse = option_value_analysis_em("SZSE")
    print(f"SZSE结果")
    print(df_szse)
    # 纵向合并两个DataFrame
    combined_df = pd.concat([df_sse, df_szse], ignore_index=True)
    return combined_df

if __name__ == "__main__":
    filename = '../tmp/option_value_analysis_em_origin.csv'

    # combined_df = fetch_all_option_value()
    # combined_df.to_csv(filename, index=False, encoding='utf-8-sig')

    combined_df = pd.read_csv(filename)

    df_with_margin = calculate_option_margin(combined_df)

    df_op_value = calculate_option_value(df_with_margin)

    filename= '../tmp/option_value_analysis_em.csv'
    df_op_value.to_csv(filename, index=False, encoding='utf-8-sig')

    # 应用虚值度过滤
    filtered_df = filter_by_virtual_value_degree(df_op_value)

    pd.set_option('display.max_columns', None)  # 显示所有列
    # pd.set_option('display.max_rows', None)  # 显示所有行（可选）
    pd.set_option('display.width', 1000)  # 设置打印宽度（可选）

    # 显示结果
    print("\n过滤后的期权数据:")
    print(filtered_df[['期权名称', '标的代码', '行权价', '标的最新价', '虚值度','虚值度阈值','最新价', '保证金', '收益率', '年化收益率']])

    filtered_df = filter_by_option_info(df_op_value,"90005539")
    print(filtered_df[['期权代码','期权名称', '标的代码', '行权价', '标的最新价', '虚值度', '虚值度阈值', '最新价','保证金', '收益率', '年化收益率']])

    filtered_df = filter_by_option_info(df_op_value,"科创板50购7")
    print(filtered_df[['期权代码','期权名称', '标的代码', '行权价', '标的最新价', '虚值度', '虚值度阈值', '最新价','保证金', '收益率', '年化收益率']])
    # df = option_value_analysis_em("SZSE")
    # print(df)

    # 恢复默认设置（可选）
    pd.reset_option('display.max_columns')
    pd.reset_option('display.max_rows')
    pd.reset_option('display.width')